/*
//
//              INTEL CORPORATION PROPRIETARY INFORMATION
//  This software is supplied under the terms of a license  agreement or
//  nondisclosure agreement with Intel Corporation and may not be copied
//  or disclosed except in  accordance  with the terms of that agreement.
//    Copyright (c) 2007-2008 Intel Corporation. All Rights Reserved.
//
//
*/

#include "umc_defs.h"
#if defined(UMC_ENABLE_AVS_VIDEO_DECODER) || \
    defined(UMC_ENABLE_AVS_VIDEO_ENCODER)

#include "umc_avs_pic.h"
#include "umc_avs_mb_info.h"

namespace UMC
{

enum
{
    PLANE_ALIGN_VALUE           = 64
};

AVSPicture::AVSPicture(void)
{
    memset(m_pPlanes8u, 0, sizeof(m_pPlanes8u));
    memset(m_pSrcPlanes8u, 0, sizeof(m_pSrcPlanes8u));
    m_iPitch = 0;
    m_colorFormat = NONE;

    memset(&m_sizeLuma, 0, sizeof(m_sizeLuma));
    memset(&m_sizeChroma, 0, sizeof(m_sizeChroma));

    m_picStructure = AVS_FRAME;

} // AVSPicture::AVSPicture(void)

AVSPicture::~AVSPicture(void)
{
    Close();

} // AVSPicture::~AVSPicture(void)

void AVSPicture::Close(void)
{
    memset(m_pPlanes8u, 0, sizeof(m_pPlanes8u));
    memset(m_pSrcPlanes8u, 0, sizeof(m_pSrcPlanes8u));
    m_iPitch = 0;
    m_colorFormat = NONE;

    memset(&m_sizeLuma, 0, sizeof(m_sizeLuma));
    memset(&m_sizeChroma, 0, sizeof(m_sizeChroma));

    m_picStructure = AVS_FRAME;

} // void AVSPicture::Close(void)

Status AVSPicture::Init(void)
{
    // release object before initialization
    Close();

    return UMC_OK;

} // Status AVSPicture::Init(void)

void AVSPicture::ResetPicture(AVS_SEQUENCE_HEADER *pSeqHeader,
                              void **pPlanes,
                              Ipp32s iPitch,
                              eAVSPicStructure picStructure)
{
    m_picStructure = picStructure;

    // initialize dimensions
    m_sizeLuma.width = pSeqHeader->horizontal_size;
    m_sizeLuma.height = pSeqHeader->vertical_size;
    m_sizeChroma = m_sizeLuma;
    switch (pSeqHeader->chroma_format)
    {
    case AVS_CHROMA_420_FORMAT:
        m_sizeChroma.height /= 2;
        m_sizeChroma.width /= 2;
        m_colorFormat = YUV420;
        break;

    case AVS_CHROMA_422_FORMAT:
        m_sizeChroma.width /= 2;
        m_colorFormat = YUV422;
        break;

    default:
        break;
    }
    if ((AVS_LOWER_FIELD == picStructure) ||
        (AVS_UPPER_FIELD == picStructure))
    {
        m_sizeLuma.height /= 2;
        m_sizeChroma.height /= 2;
    }

    // set planes' pointers
    memcpy(m_pPlanes8u, pPlanes, sizeof(void *) * 3);
    m_iPitch = iPitch;

    if ((AVS_LOWER_FIELD == picStructure) ||
        (AVS_UPPER_FIELD == picStructure))
    {
        if (AVS_LOWER_FIELD == picStructure)
        {
            // precission of samples is 8 bits
            if (1 == pSeqHeader->sample_precission)
            {
                m_pPlanes8u[0] += iPitch;
                m_pPlanes8u[1] += iPitch;
                m_pPlanes8u[2] += iPitch;
            }

        }

        m_iPitch *= 2;
    }

    // eliminate all reference pointes
    memset(m_pRefs, 0, sizeof(m_pRefs));

} // void AVSPicture::ResetPicture(AVS_SEQUENCE_HEADER *pSeqHeader,

void AVSPicture::AssignVideoPlanes(AVSSlice *pSlice)
{
    const Ipp8u *(pPlanes8u[3]);

    // advance video planes
    GetPlanesPointers(pPlanes8u,
                      (const Ipp8u **) m_pPlanes8u,
                      m_iPitch,
                      pSlice->m_recCtx.MbY,
                      m_colorFormat);

    memcpy(pSlice->m_recCtx.m_pPlanes8u, pPlanes8u, sizeof(pPlanes8u));
    pSlice->m_recCtx.m_iPitch = m_iPitch;
    memcpy(pSlice->m_debCtx.m_pPlanes8u, pPlanes8u, sizeof(pPlanes8u));
    pSlice->m_debCtx.m_iPitch = m_iPitch;

} // void AVSPicture::AssignVideoPlanes(AVSSlice *pSlice)

void AVSPicture::MarkSliceAsDone(AVSSlice *pSlice)
{
    AVSListElement<AVSSlice> *pPrev, *pCur;

    // run through the list and find the slice
    pPrev = NULL;
    pCur = m_Slices.GetHead();
    while (pCur)
    {
        if (pCur == pSlice)
            break;

        pPrev = pCur;
        pCur = pCur->GetNext();
    }
    if (NULL == pCur)
        return;

    // there is the first slice, simply move
    if (NULL == pPrev)
    {
        pCur = m_Slices.ExtractHead();
    }
    // the slice somewhere in the middle,
    // we need to repair the list
    else
    {
        pPrev->SetNext(pCur->GetNext());
        pCur->SetNext(NULL);
    }

    // add the slice into the second queue in ascending order
    {
        AVSListElement<AVSSlice> *pTemp;

        // find the adding position
        pTemp = m_SlicesSpent.GetHead();
        while (pTemp)
        {
            if ((pTemp->m_SlcHeader.slice_vertical_position <
                 pCur->m_SlcHeader.slice_vertical_position) ||
                (pTemp->m_SlcHeader.slice_vertical_position_extension <
                 pCur->m_SlcHeader.slice_vertical_position_extension))
            {
                pTemp = pTemp->GetNext();
            }
            else
            {
                break;
            }
        }

        // add slice into the list
        if (pTemp)
        {
            pCur->SetNext(pTemp->GetNext());
            pTemp->SetNext(pCur);
        }
        else
        {
            m_SlicesSpent.AddToHead(*pCur);
        }
    }

} // void AVSPicture::MarkSliceAsDone(AVSSlice *pSlice)


AVSFrame::AVSFrame(void)
{
    m_pMbInfo = NULL;
    m_pvVideoPlane = NULL;
    m_bufSize = 0;

    m_iShown = 0;

    memset(&m_dimLumaAllocated, 0, sizeof(IppiSize));
    memset(&m_dimChromaAllocated, 0, sizeof(IppiSize));
    memset(&m_dimMBAllocated, 0, sizeof(IppiSize));

} // AVSFrame::AVSFrame(void)

AVSFrame::~AVSFrame(void)
{
    Close();

} // AVSFrame::~AVSFrame(void)

void AVSFrame::Close(void)
{
    if (m_pMbInfo)
    {
        ippsFree(m_pMbInfo);
    }

    if (m_pvVideoPlane)
    {
        ippsFree(m_pvVideoPlane);
    }

    m_pMbInfo = NULL;
    m_pvVideoPlane = NULL;
    m_bufSize = 0;

    m_iShown = 0;

    memset(&m_dimLumaAllocated, 0, sizeof(IppiSize));
    memset(&m_dimChromaAllocated, 0, sizeof(IppiSize));
    memset(&m_dimMBAllocated, 0, sizeof(IppiSize));

} // void AVSFrame::Close(void)

Status AVSFrame::Init(void)
{
    IppiSize dimReq, dimReqChroma, dimMBReq;
    Ipp8u *(pPlanes8u[3]);
    bool isFramePic;

    //
    // allocate macroblock info plane
    //
    dimMBReq.width = (m_seqHeader.horizontal_size + 15) / 16;
    if (m_picHeader.picture_structure)
    {
        dimMBReq.height = (m_seqHeader.vertical_size + 15) / 16;
        isFramePic = true;
    }
    else
    {
        dimMBReq.height = 2 * ((m_seqHeader.vertical_size + 31) / 32);
        isFramePic = false;
    }
    if (dimMBReq.width * dimMBReq.height >
        m_dimMBAllocated.width * m_dimMBAllocated.height)
    {
        // deallocate if it exists
        if (m_pMbInfo)
        {
            ippsFree(m_pMbInfo);
            m_pMbInfo = NULL;
            m_dimMBAllocated.width = 0;
            m_dimMBAllocated.height = 0;
        }

        m_pMbInfo = (AVS_MB_INFO *) ippsMalloc_8u(sizeof(AVS_MB_INFO) *
                                                  dimMBReq.width *
                                                  dimMBReq.height);
        if (NULL == m_pMbInfo)
            return UMC_ERR_ALLOC;

        // set pointers to MbInfo to fields
        m_Fields[0].m_pMbInfo = m_pMbInfo;
        m_Fields[1].m_pMbInfo = m_pMbInfo + (dimMBReq.width * dimMBReq.height / 2);

        m_dimMBAllocated = dimMBReq;
    }

    //
    // allocate video planes
    //
    dimReq.width = align_value<Ipp32s> (m_seqHeader.horizontal_size, PLANE_ALIGN_VALUE) | PLANE_ALIGN_VALUE;
    if (m_picHeader.picture_structure)
        dimReq.height = align_value<Ipp32s> (m_seqHeader.vertical_size, 16);
    else
        dimReq.height = align_value<Ipp32s> (m_seqHeader.vertical_size, 32);
    switch (m_seqHeader.chroma_format)
    {
    case AVS_CHROMA_420_FORMAT:
        dimReqChroma.width = dimReq.width / 2;
        dimReqChroma.height = dimReq.height / 2;
        break;

    case AVS_CHROMA_422_FORMAT:
        dimReqChroma.width = dimReq.width / 2;
        dimReqChroma.height = dimReq.height;
        break;

    default:
        return UMC_ERR_INVALID_STREAM;
    }
    if (dimReq.width * dimReq.height +
        dimReqChroma.width * dimReqChroma.height >
        m_dimLumaAllocated.width * m_dimLumaAllocated.height +
        m_dimChromaAllocated.width * m_dimChromaAllocated.height)
    {
        Ipp32s iReqSize;

        // deallocate if it exists
        if (m_pvVideoPlane)
        {
            ippsFree(m_pvVideoPlane);
            m_pvVideoPlane = (void *) 0;
            m_bufSize = 0;

            m_dimLumaAllocated.width = 0;
            m_dimLumaAllocated.height = 0;
            m_dimChromaAllocated.width = 0;
            m_dimChromaAllocated.height = 0;
        }

        iReqSize = dimReq.width * dimReq.height +
                   (dimReqChroma.width * dimReqChroma.height * 2);
        m_pvVideoPlane = ippsMalloc_8u(iReqSize);
        if (NULL == m_pvVideoPlane)
            return UMC_ERR_ALLOC;
        m_bufSize = iReqSize;
        m_dimLumaAllocated = dimReq;
        m_dimChromaAllocated = dimReqChroma;
    }
#if defined(_DEBUG)
    memset(m_pvVideoPlane, 128, dimReq.width * dimReq.height + (dimReqChroma.width * dimReqChroma.height * 2));
#endif // defined(_DEBUG)
    pPlanes8u[0] = (Ipp8u *) m_pvVideoPlane;
    pPlanes8u[1] = pPlanes8u[0] + dimReq.width * dimReq.height;
    pPlanes8u[2] = pPlanes8u[1] + dimReq.width / 2;
    //
    // initialize picture structure
    //
    ResetPicture(&m_seqHeader,
                 (void **) pPlanes8u,
                 dimReq.width,
                 (isFramePic) ? AVS_FRAME : AVS_FIELD_PAIR);
    // initialize field just in case of PAFF
    m_Fields[0].ResetPicture(&m_seqHeader,
                             (void **) pPlanes8u,
                             dimReq.width,
                             AVS_UPPER_FIELD);
    m_Fields[0].m_picHeader = m_picHeader;

    m_Fields[1].ResetPicture(&m_seqHeader,
                             (void **) pPlanes8u,
                             dimReq.width,
                             AVS_LOWER_FIELD);
    m_Fields[1].m_picHeader = m_picHeader;
    // second field of I frame is always P frame
    if (AVS_I_PICTURE == m_picHeader.PictureType)
    {
        m_Fields[1].m_picHeader.PictureType = AVS_P_PICTURE;
    }

    return UMC_OK;

} // Status AVSFrame::Init(void)

Status AVSFrame::SetSlices(void)
{
    AVSListElement<AVSSlice> *pSlice, *pPrev;
    AVSListElement<AVSMemory> *pMemory;
    bool isFramePic;

    //
    // allocate macroblock info plane
    //
    if (m_picHeader.picture_structure)
    {
        isFramePic = true;
    }
    else
    {
        isFramePic = false;
    }

    //
    // assign MB info's and planes to slices
    //

    pSlice = m_Slices.GetHead();
    pPrev = NULL;
    pMemory = m_MemoryPieces.GetHead();
    while (pSlice)
    {
        AVS_PICTURE_HEADER *pPicHeader = &m_picHeader;
        Ipp32u fieldNum;
        Status umcRes;

        // set field number
        fieldNum = pSlice->m_SlcHeader.isSecondField;

        // get appropriate picture header
        if (false == isFramePic)
        {
            pPicHeader = &(m_Fields[fieldNum].m_picHeader);
        }

        // reset the states of the slice
        umcRes = pSlice->Reset(&m_seqHeader, pPicHeader);
        if (UMC_OK != umcRes)
            return umcRes;

        // assing macroblock plane pointer(s)
        {
            AVS_MB_INFO *pMbInfo = m_Fields[fieldNum].m_pMbInfo +
                                   pSlice->m_SlcHeader.first_mb;

            pSlice->m_decCtx.m_pMBInfo = pMbInfo;
            pSlice->m_recCtx.m_pMBInfo = pMbInfo;
            pSlice->m_debCtx.m_pMBInfo = pMbInfo;
        }

        // assign video planes to a slice
        AssignVideoPlanes(pSlice);

        // set the number of macroblocks in the slice
        if ((pPrev) &&
            (pPrev->m_SlcHeader.first_mb < pSlice->m_SlcHeader.first_mb))
        {
            pPrev->m_decCtx.MbLast = pSlice->m_SlcHeader.first_mb;
            pPrev->m_recCtx.MbLast = pSlice->m_SlcHeader.first_mb;
            pPrev->m_debCtx.MbLast = pSlice->m_SlcHeader.first_mb;
        }

        // save pointer to the previous slice
        pPrev = pSlice;

        // assign the owning picture
        if (isFramePic)
        {
            pSlice->m_pPic = this;
            pSlice = pSlice->GetNext();
        }
        else
        {
            pSlice->m_pPic = m_Fields + fieldNum;
            m_Fields[fieldNum].m_Slices.AddToTail(*m_Slices.ExtractHead());
            pSlice = m_Slices.GetHead();
        }

        // get next memory piece
        pMemory = pMemory->GetNext();
    }

    return UMC_OK;

} // Status AVSFrame::SetSlices(void)

bool AVSFrame::IsReady(void)
{
    if (AVS_FRAME == m_picStructure)
    {
        if (m_Slices.GetHead())
            return false;
    }
    else
    {
        if ((m_Fields[0].m_Slices.GetHead()) ||
            (m_Fields[1].m_Slices.GetHead()))
            return false;
    }

    return true;

} // bool AVSFrame::IsReady(void)

void AVSFrame::Reset(void)
{
    m_iShown = 0;

} // void AVSFrame::Reset(void)

void AVSFrame::AssignVideoPlanes(AVSSlice *pSlice)
{
    if (AVS_FRAME == m_picStructure)
    {
        AVSPicture::AssignVideoPlanes(pSlice);
    }
    else
    {
        if (0 == pSlice->m_SlcHeader.isSecondField)
        {
            m_Fields[0].AssignVideoPlanes(pSlice);
        }
        else
        {
            m_Fields[1].AssignVideoPlanes(pSlice);
        }
    }

} // void AVSFrame::AssignVideoPlanes(AVSSlice *pSlice)

void AVSFrame::SetReferences(AVSFrame **pRefs)
{
    memset(m_pRefs, 0, sizeof(m_pRefs));
    memset(m_Fields[0].m_pRefs, 0, sizeof(m_Fields[0].m_pRefs));
    memset(m_Fields[1].m_pRefs, 0, sizeof(m_Fields[1].m_pRefs));

    //
    // The standard doesn't set block distances to max(1, real_block_distance),
    // but we do that to avoid division on zero. Anyway zero distance is
    // incorrect.
    //

    // this is a frame
    if (AVS_B_PICTURE == m_picHeader.PictureType)
    {
        Ipp32s distIdx = m_picHeader.picture_distance * 2;
        Ipp32s blockDist;

        //
        // FRAME VARIABLES UPDATING
        //

        m_pRefs[AVS_FORWARD][0] = pRefs[1];
        m_pRefs[AVS_BACKWARD][0] = pRefs[0];

        // calculate distance indecies
        m_distIdx[AVS_FORWARD][0] = pRefs[1]->m_picHeader.picture_distance * 2;
        m_distIdx[AVS_BACKWARD][0] = pRefs[0]->m_picHeader.picture_distance * 2;

        // calculate block distances
        blockDist = distIdx -
                    m_distIdx[AVS_FORWARD][0] +
                    512;
        m_blockDist[AVS_FORWARD][0] = IPP_MAX(1, (blockDist) % 512);
        blockDist = m_distIdx[AVS_BACKWARD][0] -
                    distIdx +
                    512;
        m_blockDist[AVS_BACKWARD][0] = IPP_MAX(1, (blockDist) % 512);

        //
        // FIELD VARIABLES UPDATING
        //

        m_Fields[0].m_pRefs[AVS_FORWARD][0] = pRefs[1]->GetPicture(AVS_LOWER_FIELD);
        m_Fields[0].m_pRefs[AVS_FORWARD][1] = pRefs[1]->GetPicture(AVS_UPPER_FIELD);
        m_Fields[0].m_pRefs[AVS_BACKWARD][0] = pRefs[0]->GetPicture(AVS_UPPER_FIELD);
        m_Fields[0].m_pRefs[AVS_BACKWARD][1] = pRefs[0]->GetPicture(AVS_LOWER_FIELD);

        // calculate distance indecies
        m_Fields[0].m_distIdx[AVS_FORWARD][0] = pRefs[1]->m_picHeader.picture_distance * 2 + 1;
        m_Fields[0].m_distIdx[AVS_FORWARD][1] = pRefs[1]->m_picHeader.picture_distance * 2;
        m_Fields[0].m_distIdx[AVS_BACKWARD][0] = pRefs[0]->m_picHeader.picture_distance * 2;
        m_Fields[0].m_distIdx[AVS_BACKWARD][1] = pRefs[0]->m_picHeader.picture_distance * 2 + 1;

        // calculate block distances
        blockDist = distIdx -
                    m_Fields[0].m_distIdx[AVS_FORWARD][0] +
                    512;
        m_Fields[0].m_blockDist[AVS_FORWARD][0] = IPP_MAX(1, (blockDist) % 512);
        m_Fields[0].m_blockDist[AVS_FORWARD][1] = IPP_MAX(1, (blockDist + 1) % 512);
        blockDist = m_Fields[0].m_distIdx[AVS_BACKWARD][0] -
                    distIdx +
                    512;
        m_Fields[0].m_blockDist[AVS_BACKWARD][0] = IPP_MAX(1, (blockDist) % 512);
        m_Fields[0].m_blockDist[AVS_BACKWARD][1] = IPP_MAX(1, (blockDist + 1) % 512);

        distIdx += 1;
        m_Fields[1].m_pRefs[AVS_FORWARD][0] = m_Fields[0].m_pRefs[AVS_FORWARD][0];
        m_Fields[1].m_pRefs[AVS_FORWARD][1] = m_Fields[0].m_pRefs[AVS_FORWARD][1];
        m_Fields[1].m_pRefs[AVS_BACKWARD][0] = m_Fields[0].m_pRefs[AVS_BACKWARD][0];
        m_Fields[1].m_pRefs[AVS_BACKWARD][1] = m_Fields[0].m_pRefs[AVS_BACKWARD][1];

        // calculate distance indecies
        m_Fields[1].m_distIdx[AVS_FORWARD][0] = m_Fields[0].m_distIdx[AVS_FORWARD][0];
        m_Fields[1].m_distIdx[AVS_FORWARD][1] = m_Fields[0].m_distIdx[AVS_FORWARD][1];
        m_Fields[1].m_distIdx[AVS_BACKWARD][0] = m_Fields[0].m_distIdx[AVS_BACKWARD][0];
        m_Fields[1].m_distIdx[AVS_BACKWARD][1] = m_Fields[0].m_distIdx[AVS_BACKWARD][1];

        // calculate block distances
        blockDist = distIdx -
                    m_Fields[1].m_distIdx[AVS_FORWARD][0] +
                    512;
        m_Fields[1].m_blockDist[AVS_FORWARD][0] = IPP_MAX(1, (blockDist) % 512);
        m_Fields[1].m_blockDist[AVS_FORWARD][1] = IPP_MAX(1, (blockDist + 1) % 512);
        blockDist = m_Fields[1].m_distIdx[AVS_BACKWARD][0] -
                    distIdx +
                    512;
        m_Fields[1].m_blockDist[AVS_BACKWARD][0] = IPP_MAX(1, (blockDist) % 512);
        m_Fields[1].m_blockDist[AVS_BACKWARD][1] = IPP_MAX(1, (blockDist + 1) % 512);
    }
    else if (AVS_P_PICTURE == m_picHeader.PictureType)
    {
        Ipp32s distIdx = m_picHeader.picture_distance * 2;
        Ipp32s blockDist;

        //
        // FRAME VARIABLES UPDATING
        //

        m_pRefs[AVS_FORWARD][0] = pRefs[0];
        m_pRefs[AVS_FORWARD][1] = pRefs[1];

        // calculate distance indecies
        m_distIdx[AVS_FORWARD][0] = pRefs[0]->m_picHeader.picture_distance * 2;
        m_distIdx[AVS_FORWARD][1] = pRefs[1]->m_picHeader.picture_distance * 2;

        // calculate block distances
        blockDist = distIdx -
                    m_distIdx[AVS_FORWARD][0] +
                    512;
        m_blockDist[AVS_FORWARD][0] = IPP_MAX(1, (blockDist) % 512);
        blockDist = distIdx -
                    m_distIdx[AVS_FORWARD][1] +
                    512;
        m_blockDist[AVS_FORWARD][1] = IPP_MAX(1, (blockDist) % 512);

        //
        // FIELD VARIABLES UPDATING
        //

        m_Fields[0].m_pRefs[AVS_FORWARD][0] = pRefs[0]->GetPicture(AVS_LOWER_FIELD);
        m_Fields[0].m_pRefs[AVS_FORWARD][1] = pRefs[0]->GetPicture(AVS_UPPER_FIELD);
        m_Fields[0].m_pRefs[AVS_FORWARD][2] = pRefs[1]->GetPicture(AVS_LOWER_FIELD);
        m_Fields[0].m_pRefs[AVS_FORWARD][3] = pRefs[1]->GetPicture(AVS_UPPER_FIELD);

        // calculate distance indices
        m_Fields[0].m_distIdx[AVS_FORWARD][0] = pRefs[0]->m_picHeader.picture_distance * 2 + 1;
        m_Fields[0].m_distIdx[AVS_FORWARD][1] = pRefs[0]->m_picHeader.picture_distance * 2;
        m_Fields[0].m_distIdx[AVS_FORWARD][2] = pRefs[1]->m_picHeader.picture_distance * 2 + 1;
        m_Fields[0].m_distIdx[AVS_FORWARD][3] = pRefs[1]->m_picHeader.picture_distance * 2;

        // calculate block distances
        blockDist = distIdx -
                    m_Fields[0].m_distIdx[AVS_FORWARD][0] +
                    512;
        m_Fields[0].m_blockDist[AVS_FORWARD][0] = IPP_MAX(1, (blockDist) % 512);
        m_Fields[0].m_blockDist[AVS_FORWARD][1] = IPP_MAX(1, (blockDist + 1) % 512);
        blockDist = distIdx -
                    m_Fields[0].m_distIdx[AVS_FORWARD][2] +
                    512;
        m_Fields[0].m_blockDist[AVS_FORWARD][2] = IPP_MAX(1, (blockDist) % 512);
        m_Fields[0].m_blockDist[AVS_FORWARD][3] = IPP_MAX(1, (blockDist + 1) % 512);

        distIdx += 1;
        m_Fields[1].m_pRefs[AVS_FORWARD][0] = m_Fields + 0;
        m_Fields[1].m_pRefs[AVS_FORWARD][1] = pRefs[0]->GetPicture(AVS_LOWER_FIELD);
        m_Fields[1].m_pRefs[AVS_FORWARD][2] = pRefs[0]->GetPicture(AVS_UPPER_FIELD);
        m_Fields[1].m_pRefs[AVS_FORWARD][3] = pRefs[1]->GetPicture(AVS_LOWER_FIELD);

        // calculate distance indices
        m_Fields[1].m_distIdx[AVS_FORWARD][0] = m_picHeader.picture_distance * 2;
        m_Fields[1].m_distIdx[AVS_FORWARD][1] = pRefs[0]->m_picHeader.picture_distance * 2 + 1;
        m_Fields[1].m_distIdx[AVS_FORWARD][2] = pRefs[0]->m_picHeader.picture_distance * 2;
        m_Fields[1].m_distIdx[AVS_FORWARD][3] = pRefs[1]->m_picHeader.picture_distance * 2 + 1;

        // calculate block distances
        blockDist = distIdx -
                    m_Fields[1].m_distIdx[AVS_FORWARD][1] +
                    512;
        m_Fields[1].m_blockDist[AVS_FORWARD][0] = (512 + 1) % 512;
        m_Fields[1].m_blockDist[AVS_FORWARD][1] = IPP_MAX(1, (blockDist) % 512);
        m_Fields[1].m_blockDist[AVS_FORWARD][2] = IPP_MAX(1, (blockDist + 1) % 512);
        blockDist = distIdx -
                    m_Fields[1].m_distIdx[AVS_FORWARD][3] +
                    512;
        m_Fields[1].m_blockDist[AVS_FORWARD][3] = IPP_MAX(1, (blockDist) % 512);
    }
    else
    {
        m_Fields[1].m_pRefs[AVS_FORWARD][0] = m_Fields + 0;

        // calculate distance indices
        m_Fields[1].m_distIdx[AVS_FORWARD][0] = m_picHeader.picture_distance * 2;

        // calculate block distances
        m_Fields[1].m_blockDist[AVS_FORWARD][0] = (512 + 1) % 512;
    }

} // void AVSFrame::SetReferences(AVSFrame **pRefs)

void AVSFrame::RemoveSlices(AVSList<AVSSlice> *pList)
{
    if (AVS_FRAME == m_picStructure)
    {
        pList->AddToTail(m_Slices);
        pList->AddToTail(m_SlicesSpent);
    }
    else
    {
        pList->AddToTail(m_Fields[0].m_Slices);
        pList->AddToTail(m_Fields[0].m_SlicesSpent);
        pList->AddToTail(m_Fields[1].m_Slices);
        pList->AddToTail(m_Fields[1].m_SlicesSpent);
    }

} // void AVSFrame::RemoveSlice(AVSList<AVSSlice> *pList)

void AVSFrame::RemoveMemoryPieces(AVSList<AVSMemory> *pList)
{
    pList->AddToTail(m_MemoryPieces);

} // void AVSFrame::RemoveMemoryPieces(AVSList<AVSMemory> *pList)

Ipp32s GetDecReadyLevel(AVSPicture *pPic)
{
    AVSListElement<AVSSlice> *pSlice;
    Ipp32s refReadyLevel;

    refReadyLevel = pPic->m_sizeLuma.height;

    // get picture's slice list
    pSlice = pPic->m_Slices.GetHead();

    if (pSlice)
    {
        refReadyLevel = IPP_MIN(refReadyLevel, pSlice->m_decCtx.MbY * 16);
    }

    return refReadyLevel;

} // Ipp32s GetDecReadyLevel(AVSPicture *pPic)

Ipp32s GetRecReadyLevel(AVSPicture *pPic)
{
    AVSListElement<AVSSlice> *pSlice;
    Ipp32s refReadyLevel;

    refReadyLevel = pPic->m_sizeLuma.height;

    // get picture's slice list
    pSlice = pPic->m_Slices.GetHead();

    if (pSlice)
    {
        if (pSlice->m_recCtx.m_pPicHeader->loop_filter_disable)
        {
            refReadyLevel = IPP_MIN(refReadyLevel, pSlice->m_recCtx.MbY * 16);
        }
        else
        {
            refReadyLevel = IPP_MIN(refReadyLevel, pSlice->m_debCtx.MbY * 16 - 4);
        }
    }

    return refReadyLevel;

} // Ipp32s GetRecReadyLevel(AVSPicture *pPic)

} // namespace UMC

#endif // defined(UMC_ENABLE_AVS_VIDEO_DECODER) ||
